package com.dycong.common.common.lock.v3;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * Created by WangYongHuang on 2017/8/22.
 */
@Component
public class RedisService {
    private static final Logger logger = LoggerFactory.getLogger(RedisService.class);


    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    //拿锁
    private static final String LUA_SCRIPT_LOCK = "return redis.call('SET', KEYS[1], ARGV[1], 'NX', 'EX', ARGV[2]) ";
    private static RedisScript<String> scriptLock = new DefaultRedisScript<>(LUA_SCRIPT_LOCK, String.class);

    //释放锁
    private static final String LUA_SCRIPT_UNLOCK = "if (redis.call('GET', KEYS[1]) == ARGV[1]) then return redis.call('DEL',KEYS[1]) else " + "return 0 " + "end";
    private static RedisScript<Long> scriptUnlock = new DefaultRedisScript<>(LUA_SCRIPT_UNLOCK, Long.class);


    private static String getCacheKey(String key, JedisCacheGroup group) {
        return group.getGroupName() + JedisUtils.SPARTOR_GROP + key;
    }

    public void set(String key, String value, JedisCacheGroup group) {
        String cacheKey = getCacheKey(key, group);
        ValueOperations<String, String> ops = this.stringRedisTemplate.opsForValue();
        ops.set(cacheKey, value);
        if (group.getTimeOut() > 0) {
            stringRedisTemplate.expire(cacheKey, group.getTimeOut(), TimeUnit.SECONDS);
        }
    }

    /***
     * 加锁
     * @param key
     * @param group
     * @return 加锁返回true
     * 已经存在锁，返回false
     */
    public boolean lock(String key, String value, JedisCacheGroup group) {
        String cacheKey = getCacheKey(key, group);
        String execute = stringRedisTemplate.execute(scriptLock,
                Collections.singletonList(cacheKey),
                value,
                group.getTimeOut().toString());
        return execute != null && "OK".equals(execute);
    }

    /**
     * 解锁
     *
     * @param key
     * @param value
     * @param group
     * @return
     */
    public boolean unLock(String key, String value, JedisCacheGroup group) {
        String cacheKey = getCacheKey(key, group);
        Long result = stringRedisTemplate.execute(scriptUnlock,
                Collections.singletonList(cacheKey),
                value);
        return result != null && result == 1;
    }

    /**
     * 放入并设置超时时间
     *
     * @param key
     * @param group
     * @param step
     */
    public void increase(String key, JedisCacheGroup group, Integer step) {
        String cacheKey = getCacheKey(key, group);
        ValueOperations<String, String> ops = this.stringRedisTemplate.opsForValue();
        ops.increment(cacheKey, step);
        if (group.getTimeOut() > 0) {
            stringRedisTemplate.expire(cacheKey, group.getTimeOut(), TimeUnit.SECONDS);
        }
    }

    /**
     * 放入并设置自定义超时时间
     *
     * @param key
     * @param group
     * @param step
     */
    public void increase(String key, JedisCacheGroup group, Integer step, Integer timeout) {
        String cacheKey = getCacheKey(key, group);
        ValueOperations<String, String> ops = this.stringRedisTemplate.opsForValue();
        ops.increment(cacheKey, step);
        if (group.getTimeOut() > 0) {
            stringRedisTemplate.expire(cacheKey, timeout, TimeUnit.SECONDS);
        }
    }

    public String get(String key, JedisCacheGroup group) {
        String cacheKey = getCacheKey(key, group);
        return stringRedisTemplate.opsForValue().get(cacheKey);
    }

    public boolean isExists(String key, JedisCacheGroup group) {
        String cacheKey = getCacheKey(key, group);
        return this.stringRedisTemplate.hasKey(cacheKey);
    }

    public boolean isHashExistsKey(String key, String field, JedisCacheGroup group) {
        String cacheKey = this.getCacheKey(key, group);
        return stringRedisTemplate.opsForHash().hasKey(cacheKey, field);
    }

    public String getHashValue(String key, String field, JedisCacheGroup group) {
        String cacheKey = this.getCacheKey(key, group);
        return (String) stringRedisTemplate.opsForHash().get(cacheKey, field);
    }


    public Integer getLiveTime(String key, JedisCacheGroup group) {
        String cacheKey = getCacheKey(key, group);
        return this.stringRedisTemplate.getExpire(cacheKey).intValue();
    }


    public void delete(String key, JedisCacheGroup group) {
        String cacheKey = getCacheKey(key, group);
        this.stringRedisTemplate.delete(cacheKey);
    }

    public void hset(String key, JedisCacheGroup group, Map<String, Object> datas) {
        String cacheKey = this.getCacheKey(key, group);
        HashOperations<String, Object, Object> operations = stringRedisTemplate.opsForHash();
        operations.putAll(cacheKey, datas);
    }

    public Object hget(String key, JedisCacheGroup group, String field) {
        String cacheKey = this.getCacheKey(key, group);
        HashOperations<String, Object, Object> operations = stringRedisTemplate.opsForHash();
        return operations.get(cacheKey, field);
    }

}
